#[macro_export]
macro_rules! unit {
    (@id $($items:tt)*) => {
        $($items)*
    };

    (@enum_cc $callback:ident!{$($prefix:tt)*}) => {
        $callback! { $($prefix)*
            pub enum Unit {
                LittleFairy(LittleFairy)
            }
        }
    };

    (@enum_pairs_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        pub enum Unit {
            $($variant_name:ident($variant_type:ty))+
        }
    ) => {
        unit! { @enum_pairs_reduce
            $reducer! {$($reducer_prefix)*}
            $cc! {$($cc_prefix)*}
            ($(($variant_name,$variant_type)),+)
            ()
        }
    };

    (@enum_pairs_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        (
            ($variant_name:ident,$variant_type:ty)
            $(,($tail_variant_name:ident,$tail_variant_type:ty))*
        )
        ($($result:tt)*)
    ) => {
        $reducer! { $($reducer_prefix)*
            unit!{@enum_pairs_reduce
                $reducer! {$($reducer_prefix)*}
                $cc! {$($cc_prefix)*}
                ($(($tail_variant_name, $tail_variant_type)),*)
            }
            ($($result)*)
            $variant_name $variant_type
        }
    };

    (@enum_pairs_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        ()
        ($($result:tt)*)
    ) => {
        $cc!{$($cc_prefix)*
            $($result)*
        }
    };

    (@enum_convert_reducer
        $cc:ident!{$($prefix:tt)*}
        ($($result:tt)*)
        $variant_name:ident $variant_type:ty
    ) => {
        $cc! { $($prefix)*
            (
                $($result)*
                impl From<$variant_type> for super::Unit {
                    fn from(s: $variant_type) -> Self {
                        crate::logic::Unit::$variant_name(s)
                    }
                }
            )
         }
    };

    (@enum) => {
        unit!{@enum_cc unit!{@id}}
        unit!{@enum_cc
            unit!{@enum_pairs_reduce
                unit!{@enum_convert_reducer}
                unit!{@id}
            }
        }
    };

    (@trait_fns_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        ($($head:tt)*)
        (;$($tail:tt)*)
        ($($result:tt)*)
    ) => {
        $reducer! { $($reducer_prefix)*
            unit!{@trait_fns_reduce
                $reducer! {$($reducer_prefix)*}
                $cc! {$($cc_prefix)*}
                ()
                ($($tail)*)
            }
            ($($result)*)
            $($head)*
        }
    };

    (@trait_fns_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        ($($head:tt)*)
        ($next_token:tt$($tail:tt)*)
        ($($result:tt)*)
    ) => {
        unit!{@trait_fns_reduce
            $reducer!{$($reducer_prefix)*}
            $cc!{$($cc_prefix)*}
            ($($head)*$next_token)
            ($($tail)*)
            ($($result)*)
        }
    };

    (@trait_fns_reduce
        $reducer:ident!{$($reducer_prefix:tt)*}
        $cc:ident!{$($cc_prefix:tt)*}
        ()
        ()
        ($($result:tt)*)
    ) => {
        $cc!{$($cc_prefix)*
            $($result)*
        }
    };

    (@con
        $cc:ident!{$($cc_prefix:tt)*}
        $($a:tt)*
    ) => {
        $cc!{$($cc_prefix)*
        ($($a)*)
        }
    };

    (@trait_fn_reducer
        $cc:ident!{$($cc_prefix:tt)*}
        ($($result:tt)*)
        fn $function_name:ident($($arg_tt:tt)*) -> $($return_type:tt)+
     ) => {
        unit!{@trait_fn
            unit!{@con
                $cc!{$($cc_prefix)*}
                $($result)*
            }
            (fn $function_name($($arg_tt)*) -> $($return_type)+)
        }
    };

    (@trait_fn
        $cc:ident!{$($prefix:tt)*}
        (fn $function_name:ident(&self$($arg_tt:tt)*) -> $($return_type:tt)+)
    ) => {
        unit!{@enum_cc
            unit!{@enum_pairs_reduce
                unit!{@trait_fn_match_arms_reducer
                    (fn $function_name(&self$($arg_tt)*) -> $($return_type)+)
                }
                unit!{@trait_fn_packer
                    $cc!{$($prefix)*}
                    (fn $function_name(&self$($arg_tt)*) -> $($return_type)+)
                }
            }
        }
    };

    (@trait_fn_packer
        $cc:ident!{$($prefix:tt)*}
        (fn $function_name:ident(&self$($arg_tt:tt)*) -> $($return_type:tt)+)
        $($arms:tt)*
    ) => {
        $cc! {$($prefix)*
            fn $function_name(&self$($arg_tt)*) -> $($return_type)+ {
                match self {
                    $($arms)*
                }
            }
        }
    };

    (@trait_fn_variant_args
        $cc:ident!{$($prefix:tt)*}
        &self$(,$arg:tt)*
    ) => {
        unit!{@trait_fn_variant_args 
            $cc!{$($prefix)*}
            $(,$arg)*
        }
    };

    (@trait_fn_variant_args
        $cc:ident!{$($prefix:tt)*}
        ,$($arg_name:ident:$arg_type:ty)*
    ) => {
        unit!{@trait_fn_variant_args 
            $cc!{$($prefix)*}
            $($arg_name),*
        }
    };

    (@trait_fn_variant_args
        $cc:ident!{$($prefix:tt)*}
    ) => {
        $cc!{$($prefix)*}
    };

    (@trait_fn_match_arms_reducer
        (fn $function_name:ident(&self$(,$arg_name:ident:$arg_type:ty)*) -> Self)
        $cc:ident!{$($prefix:tt)*}
        ($($result:tt)*)
        $variant_name:ident $variant_type:ty
    ) => {
        $cc! { $($prefix)*
            (
                $($result)*
                Unit::$variant_name(variant) => variant.$function_name($($arg_name),*).into(),
            )
         }
    };

    (@trait_fn_match_arms_reducer
        (fn $function_name:ident(&self$(,$arg_name:ident:$arg_type:ty)*) -> $($return_type:tt)+)
        $cc:ident!{$($prefix:tt)*}
        ($($result:tt)*)
        $variant_name:ident $variant_type:ty
    ) => {
        $cc! { $($prefix)*
            (
                $($result)*
                Unit::$variant_name(variant) => variant.$function_name($($arg_name),*),
            )
         }
    };

    (
        pub trait $trait:ident {
            $($tt:tt)*
        }
    ) => {
        pub trait $trait {
            $($tt)*
        }

        impl $trait for Unit {
            unit!{@trait_fns_reduce
                unit!{@trait_fn_reducer }
                unit!{@id}
                ()
                ($($tt)*)
                ()
            }
        }
    };

    () => {
        unit!{@enum}
    };
}

